package at.ac.tuwien.ame;

import at.ac.tuwien.ame.FTLCodeGenerator;
import at.ac.tuwien.ame.FTLCompiler;
import at.ac.tuwien.ame.FTLUtils;
import com.google.common.base.Objects;
import com.google.common.collect.Iterables;
import ftldsl.Assignment;
import ftldsl.ComparisonOperator;
import ftldsl.ConstructorFunction;
import ftldsl.Expression;
import ftldsl.For;
import ftldsl.Function;
import ftldsl.FunctionCall;
import ftldsl.If;
import ftldsl.LogicalOperator;
import ftldsl.ModelElement;
import ftldsl.Parameter;
import ftldsl.ReturnStatement;
import ftldsl.Statement;
import ftldsl.Transformation;
import ftldsl.Variable;
import java.util.Arrays;
import java.util.List;
import javax.inject.Inject;
import org.eclipse.emf.codegen.util.ImportManager;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtend2.lib.StringConcatenation;
import org.eclipse.xtext.xbase.lib.Conversions;
import org.eclipse.xtext.xbase.lib.Exceptions;
import org.eclipse.xtext.xbase.lib.IntegerRange;
import org.eclipse.xtext.xbase.lib.IterableExtensions;
import org.eclipse.xtext.xbase.lib.StringExtensions;

/**
 * Compiles FTL to idiomatic Java source code.
 */
@SuppressWarnings("all")
public class JavaCodeGenerator extends FTLCodeGenerator {
  @Inject
  protected ImportManager importManager;
  
  /**
   * Sets or adds a new variable within the given scope.
   */
  public String V_SET(final String name, final EObject scope, final Expression e) {
    String _generateExpression = this.generateExpression(e);
    String _V_SET = FTLCompiler.V_SET(name, scope, _generateExpression);
    return _V_SET;
  }
  
  /**
   * Attempts to resolve the return type of the given expression.
   */
  public String resolveReturnType(final String returnType) {
    String _xifexpression = null;
    boolean _or = false;
    boolean _equals = Objects.equal(returnType, null);
    if (_equals) {
      _or = true;
    } else {
      boolean _equals_1 = returnType.equals("Nothing");
      _or = (_equals || _equals_1);
    }
    if (_or) {
      _xifexpression = "void";
    } else {
      String _importedName = this.importManager.getImportedName(returnType, true);
      _xifexpression = _importedName;
    }
    return _xifexpression;
  }
  
  /**
   * Attempts to resolve the return type of the given expression.
   */
  public String resolveReturnType(final Expression e) {
    String _xifexpression = null;
    boolean _equals = Objects.equal(e, null);
    if (_equals) {
      String _resolveReturnType = this.resolveReturnType("Nothing");
      _xifexpression = _resolveReturnType;
    } else {
      String _xifexpression_1 = null;
      Object _value = e.getValue();
      boolean _notEquals = (!Objects.equal(_value, null));
      if (_notEquals) {
        String _xblockexpression = null;
        {
          Object _value_1 = e.getValue();
          String _string = _value_1.toString();
          final String clazz = FTLCompiler.inferType(_string);
          boolean _equals_1 = Objects.equal(clazz, null);
          if (_equals_1) {
            boolean _and = false;
            Object _value_2 = e.getValue();
            String _string_1 = _value_2.toString();
            boolean _startsWith = _string_1.startsWith("\"");
            if (!_startsWith) {
              _and = false;
            } else {
              Object _value_3 = e.getValue();
              String _string_2 = _value_3.toString();
              boolean _endsWith = _string_2.endsWith("\"");
              _and = (_startsWith && _endsWith);
            }
            if (_and) {
              return "java.lang.String";
            } else {
              try {
                Object _value_4 = e.getValue();
                String _string_3 = _value_4.toString();
                Integer.parseInt(_string_3);
                return "java.lang.Integer";
              } catch (final Throwable _t) {
                if (_t instanceof Exception) {
                  final Exception ex = (Exception)_t;
                } else {
                  throw Exceptions.sneakyThrow(_t);
                }
              }
            }
            StringConcatenation _builder = new StringConcatenation();
            _builder.append("Cannot determine type for expression ");
            Object _value_5 = e.getValue();
            _builder.append(_value_5, "");
            IllegalArgumentException _illegalArgumentException = new IllegalArgumentException(_builder.toString());
            throw _illegalArgumentException;
          }
          String _string_4 = clazz.toString();
          _xblockexpression = (_string_4);
        }
        _xifexpression_1 = _xblockexpression;
      } else {
        String _xifexpression_2 = null;
        FunctionCall _invocation = e.getInvocation();
        boolean _notEquals_1 = (!Objects.equal(_invocation, null));
        if (_notEquals_1) {
          FunctionCall _invocation_1 = e.getInvocation();
          Function _callee = _invocation_1.getCallee();
          String _returnType = _callee.getReturnType();
          String _resolveReturnType_1 = this.resolveReturnType(_returnType);
          _xifexpression_2 = _resolveReturnType_1;
        }
        _xifexpression_1 = _xifexpression_2;
      }
      _xifexpression = _xifexpression_1;
    }
    return _xifexpression;
  }
  
  /**
   * Generates the code for the given {@link Transformation}.
   */
  public CharSequence generateTransformation(final Transformation t) {
    String _xblockexpression = null;
    {
      ModelElement _context = t.getContext();
      FTLCompiler.V_SET("instance", t, _context);
      ModelElement _context_1 = t.getContext();
      String _name = _context_1.getName();
      final String packageName = FTLUtils.getPackage(_name);
      StringConcatenation _builder = new StringConcatenation();
      _builder.append("/**");
      _builder.newLine();
      _builder.append(" ");
      _builder.append("* GENERATED BY FTL");
      _builder.newLine();
      _builder.append(" ");
      _builder.append("*/");
      _builder.newLine();
      _builder.append("package ");
      _builder.append(packageName, "");
      _builder.append(";");
      _builder.newLineIfNotEmpty();
      StringBuilder _stringBuilder = new StringBuilder(_builder.toString());
      final StringBuilder code = _stringBuilder;
      this.importManager.markImportLocation(code);
      StringConcatenation _builder_1 = new StringConcatenation();
      _builder_1.newLine();
      _builder_1.append("/**");
      _builder_1.newLine();
      _builder_1.append(" ");
      _builder_1.append("* Contains transformations for {@link ");
      ModelElement _context_2 = t.getContext();
      String _name_1 = _context_2.getName();
      String _unqualifiedName = FTLUtils.getUnqualifiedName(_name_1);
      _builder_1.append(_unqualifiedName, " ");
      _builder_1.append("}.");
      _builder_1.newLineIfNotEmpty();
      _builder_1.append(" ");
      _builder_1.append("*/");
      _builder_1.newLine();
      _builder_1.append("public class ");
      ModelElement _context_3 = t.getContext();
      String _name_2 = _context_3.getName();
      String _unqualifiedName_1 = FTLUtils.getUnqualifiedName(_name_2);
      _builder_1.append(_unqualifiedName_1, "");
      _builder_1.append("Transformation {");
      _builder_1.newLineIfNotEmpty();
      _builder_1.append("\t");
      _builder_1.append("static ");
      ModelElement _context_4 = t.getContext();
      String _name_3 = _context_4.getName();
      String _importedName = this.importManager.getImportedName(_name_3, true);
      _builder_1.append(_importedName, "	");
      _builder_1.append(" instance;");
      _builder_1.newLineIfNotEmpty();
      _builder_1.newLine();
      _builder_1.append("\t");
      _builder_1.append("public static void main(String... args) throws Exception {");
      _builder_1.newLine();
      _builder_1.append("\t\t");
      _builder_1.append("if (args.length == 0) {");
      _builder_1.newLine();
      _builder_1.append("\t\t\t");
      _builder_1.append("System.out.println(\"Usage: \" + ");
      ModelElement _context_5 = t.getContext();
      String _name_4 = _context_5.getName();
      String _unqualifiedName_2 = FTLUtils.getUnqualifiedName(_name_4);
      _builder_1.append(_unqualifiedName_2, "			");
      _builder_1.append("Transformation.class.getSimpleName() + \" MODEL\");");
      _builder_1.newLineIfNotEmpty();
      _builder_1.append("\t\t");
      _builder_1.append("}");
      _builder_1.newLine();
      _builder_1.newLine();
      _builder_1.append("\t\t");
      ModelElement _context_6 = t.getContext();
      String _name_5 = _context_6.getName();
      String _unqualifiedName_3 = FTLUtils.getUnqualifiedName(_name_5);
      _builder_1.append(_unqualifiedName_3, "		");
      _builder_1.append("Package.eINSTANCE.eClass();");
      _builder_1.newLineIfNotEmpty();
      _builder_1.append("\t\t");
      _builder_1.append("Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;");
      _builder_1.newLine();
      _builder_1.append("\t\t");
      _builder_1.append("Map<String, Object> map = reg.getExtensionToFactoryMap();");
      _builder_1.newLine();
      _builder_1.append("\t\t");
      _builder_1.append("map.put(\"xmi\", new XMIResourceFactoryImpl());");
      _builder_1.newLine();
      _builder_1.newLine();
      _builder_1.append("\t\t");
      _builder_1.append("ResourceSet resSet = new ResourceSetImpl();");
      _builder_1.newLine();
      _builder_1.append("\t\t");
      _builder_1.append("Resource resource = resSet.getResource(URI.createURI(args[0]), true);");
      _builder_1.newLine();
      _builder_1.newLine();
      _builder_1.append("\t\t");
      ModelElement _context_7 = t.getContext();
      String _name_6 = _context_7.getName();
      _builder_1.append(_name_6, "		");
      _builder_1.append("Transformation transformation = new ");
      ModelElement _context_8 = t.getContext();
      String _name_7 = _context_8.getName();
      _builder_1.append(_name_7, "		");
      _builder_1.append("Transformation();");
      _builder_1.newLineIfNotEmpty();
      _builder_1.append("\t\t");
      _builder_1.append("List<Method> methods = new ArrayList<Method>();");
      _builder_1.newLine();
      _builder_1.append("\t\t");
      _builder_1.append("for (Method method : transformation.getClass().getMethods()) {");
      _builder_1.newLine();
      _builder_1.append("\t\t\t");
      _builder_1.append("if (method.getParameterTypes().length == 1 && method.getParameterTypes()[0] == ");
      ModelElement _context_9 = t.getContext();
      String _name_8 = _context_9.getName();
      String _unqualifiedName_4 = FTLUtils.getUnqualifiedName(_name_8);
      _builder_1.append(_unqualifiedName_4, "			");
      _builder_1.append(".class) {");
      _builder_1.newLineIfNotEmpty();
      _builder_1.append("\t\t\t\t");
      _builder_1.append("methods.add(method);");
      _builder_1.newLine();
      _builder_1.append("\t\t\t");
      _builder_1.append("}");
      _builder_1.newLine();
      _builder_1.append("\t\t");
      _builder_1.append("}");
      _builder_1.newLine();
      _builder_1.newLine();
      _builder_1.append("\t\t");
      _builder_1.append("for (EObject obj : resource.getContents()) {");
      _builder_1.newLine();
      _builder_1.append("\t\t\t");
      _builder_1.append("if (obj instanceof ");
      ModelElement _context_10 = t.getContext();
      String _name_9 = _context_10.getName();
      String _unqualifiedName_5 = FTLUtils.getUnqualifiedName(_name_9);
      _builder_1.append(_unqualifiedName_5, "			");
      _builder_1.append(") {");
      _builder_1.newLineIfNotEmpty();
      _builder_1.append("\t\t\t\t");
      _builder_1.append("for (Method method : methods) {");
      _builder_1.newLine();
      _builder_1.append("\t\t\t\t\t");
      _builder_1.append("method.invoke(transformation, obj);");
      _builder_1.newLine();
      _builder_1.append("\t\t\t\t");
      _builder_1.append("}");
      _builder_1.newLine();
      _builder_1.append("\t\t\t");
      _builder_1.append("}");
      _builder_1.newLine();
      _builder_1.append("\t\t");
      _builder_1.append("}");
      _builder_1.newLine();
      _builder_1.append("\t");
      _builder_1.append("}");
      _builder_1.newLine();
      _builder_1.newLine();
      {
        EList<Function> _functions = t.getFunctions();
        boolean _hasElements = false;
        for(final Function f : _functions) {
          if (!_hasElements) {
            _hasElements = true;
          } else {
            _builder_1.appendImmediate("\n", "	");
          }
          _builder_1.append("\t");
          CharSequence _generateFunction = this.generateFunction(f);
          _builder_1.append(_generateFunction, "	");
          _builder_1.newLineIfNotEmpty();
        }
      }
      _builder_1.append("}");
      _builder_1.newLine();
      code.append(_builder_1.toString());
      FTLCompiler.V_EVICT(null);
      this.importManager.emitSortedImports();
      String _string = code.toString();
      _xblockexpression = (_string);
    }
    return _xblockexpression;
  }
  
  /**
   * Generates the code for the given {@link Function}.
   */
  protected CharSequence _generateFunction(final Function f) {
    CharSequence _xblockexpression = null;
    {
      Parameter _input = f.getInput();
      String _name = _input.getName();
      Parameter _input_1 = f.getInput();
      ModelElement _element = _input_1.getElement();
      FTLCompiler.V_SET(_name, f, _element);
      String _returnType = f.getReturnType();
      final String returnType = this.resolveReturnType(_returnType);
      StringConcatenation _builder = new StringConcatenation();
      _builder.append("public ");
      String _importedName = this.importManager.getImportedName(returnType, true);
      _builder.append(_importedName, "");
      _builder.append(" ");
      String _name_1 = f.getName();
      _builder.append(_name_1, "");
      _builder.append("(");
      Parameter _input_2 = f.getInput();
      ModelElement _element_1 = _input_2.getElement();
      String _name_2 = _element_1.getName();
      String _importedName_1 = this.importManager.getImportedName(_name_2, true);
      _builder.append(_importedName_1, "");
      _builder.append(" ");
      Parameter _input_3 = f.getInput();
      String _name_3 = _input_3.getName();
      _builder.append(_name_3, "");
      _builder.append(") {");
      _builder.newLineIfNotEmpty();
      {
        Expression _guard = f.getGuard();
        boolean _notEquals = (!Objects.equal(_guard, null));
        if (_notEquals) {
          _builder.append("\t");
          _builder.append("if (!(");
          Expression _guard_1 = f.getGuard();
          String _serialize = this.serializer.serialize(_guard_1);
          _builder.append(_serialize, "	");
          _builder.append(")) {");
          _builder.newLineIfNotEmpty();
          _builder.append("\t");
          _builder.append("\t");
          _builder.append("return");
          {
            boolean _equals = "void".equals(returnType);
            boolean _not = (!_equals);
            if (_not) {
              _builder.append(" null");
            }
          }
          _builder.append(";");
          _builder.newLineIfNotEmpty();
          _builder.append("\t");
          _builder.append("}");
          _builder.newLine();
        }
      }
      {
        Parameter _input_4 = f.getInput();
        ModelElement _element_2 = _input_4.getElement();
        String _name_4 = _element_2.getName();
        EObject _eContainer = f.eContainer();
        ModelElement _context = ((Transformation) _eContainer).getContext();
        String _name_5 = _context.getName();
        boolean _equals_1 = Objects.equal(_name_4, _name_5);
        if (_equals_1) {
          _builder.append("\t");
          EObject _eContainer_1 = f.eContainer();
          ModelElement _context_1 = ((Transformation) _eContainer_1).getContext();
          String _name_6 = _context_1.getName();
          _builder.append(_name_6, "	");
          _builder.append("Transformation.instance = ");
          Parameter _input_5 = f.getInput();
          String _name_7 = _input_5.getName();
          _builder.append(_name_7, "	");
          _builder.append(";");
          _builder.newLineIfNotEmpty();
          _builder.newLine();
        }
      }
      {
        EList<Statement> _statements = f.getStatements();
        for(final Statement s : _statements) {
          _builder.append("\t");
          CharSequence _generateStatement = this.generateStatement(s);
          _builder.append(_generateStatement, "	");
          {
            if ((s instanceof FunctionCall)) {
              _builder.append(";");
            }
          }
          _builder.newLineIfNotEmpty();
        }
      }
      _builder.append("}");
      final CharSequence code = _builder;
      FTLCompiler.V_EVICT(f);
      _xblockexpression = (code);
    }
    return _xblockexpression;
  }
  
  /**
   * Generates the code for the given {@link ConstructorFunction}.
   */
  protected CharSequence _generateFunction(final ConstructorFunction c) {
    CharSequence _xblockexpression = null;
    {
      Parameter _input = c.getInput();
      String _name = _input.getName();
      Parameter _input_1 = c.getInput();
      ModelElement _element = _input_1.getElement();
      FTLCompiler.V_SET(_name, c, _element);
      String _returnType = c.getReturnType();
      final String type = this.resolveReturnType(_returnType);
      this.importManager.addImport("org.eclipse.emf.ecore.*");
      StringConcatenation _builder = new StringConcatenation();
      _builder.append("public ");
      _builder.append(type, "");
      _builder.append(" ");
      String _name_1 = c.getName();
      _builder.append(_name_1, "");
      _builder.append("(");
      Parameter _input_2 = c.getInput();
      ModelElement _element_1 = _input_2.getElement();
      String _name_2 = _element_1.getName();
      String _importedName = this.importManager.getImportedName(_name_2, true);
      _builder.append(_importedName, "");
      _builder.append(" ");
      Parameter _input_3 = c.getInput();
      String _name_3 = _input_3.getName();
      _builder.append(_name_3, "");
      _builder.append(") {");
      _builder.newLineIfNotEmpty();
      _builder.append("\t");
      String _importedName_1 = this.importManager.getImportedName(type, true);
      _builder.append(_importedName_1, "	");
      _builder.append(" ");
      String _firstLower = StringExtensions.toFirstLower(type);
      _builder.append(_firstLower, "	");
      _builder.append(" = ");
      _builder.append(this.modelName, "	");
      _builder.append("Factory.eINSTANCE.create");
      _builder.append(type, "	");
      _builder.append("();");
      _builder.newLineIfNotEmpty();
      {
        EList<Statement> _statements = c.getStatements();
        for(final Statement s : _statements) {
          {
            if ((s instanceof Assignment)) {
              _builder.append("\t");
              String _firstLower_1 = StringExtensions.toFirstLower(type);
              _builder.append(_firstLower_1, "	");
              _builder.append(".eSet(");
              _builder.append(this.modelName, "	");
              _builder.append("Package.eINSTANCE.get");
              _builder.append(type, "	");
              _builder.append("_");
              ModelElement _modelVariable = ((Assignment) s).getModelVariable();
              String _name_4 = _modelVariable.getName();
              String _firstUpper = StringExtensions.toFirstUpper(_name_4);
              _builder.append(_firstUpper, "	");
              _builder.append("(), ");
              Expression _assignment = ((Assignment) s).getAssignment();
              String _generateExpression = this.generateExpression(_assignment);
              _builder.append(_generateExpression, "	");
              _builder.append(");");
              _builder.newLineIfNotEmpty();
              _builder.append("\t");
            } else {
              CharSequence _generateStatement = this.generateStatement(s);
              _builder.append(_generateStatement, "	");
            }
          }
          _builder.newLineIfNotEmpty();
        }
      }
      _builder.append("\t");
      _builder.append("return ");
      String _firstLower_2 = StringExtensions.toFirstLower(type);
      _builder.append(_firstLower_2, "	");
      _builder.append(";");
      _builder.newLineIfNotEmpty();
      _builder.append("}");
      _builder.newLine();
      final CharSequence code = _builder;
      FTLCompiler.V_EVICT(c);
      _xblockexpression = (code);
    }
    return _xblockexpression;
  }
  
  /**
   * Generates the code for the given {@link FunctionCall}.
   */
  protected CharSequence _generateStatement(final FunctionCall f) {
    StringConcatenation _builder = new StringConcatenation();
    Function _callee = f.getCallee();
    String _name = _callee.getName();
    _builder.append(_name, "");
    _builder.append("(");
    ModelElement _parameter = f.getParameter();
    String _name_1 = _parameter.getName();
    _builder.append(_name_1, "");
    _builder.append(")");
    return _builder;
  }
  
  /**
   * Generates the code for the given {@link ReturnStatement}.
   */
  protected CharSequence _generateStatement(final ReturnStatement r) {
    StringConcatenation _builder = new StringConcatenation();
    _builder.append("return ");
    Expression _returnExpression = r.getReturnExpression();
    String _generateExpression = this.generateExpression(_returnExpression);
    _builder.append(_generateExpression, "");
    _builder.append(";");
    return _builder;
  }
  
  /**
   * Generates the code for the given {@link Variable}.
   */
  protected CharSequence _generateStatement(final Variable v) {
    CharSequence _xblockexpression = null;
    {
      String _name = v.getName();
      EObject _eContainer = v.eContainer();
      Expression _value = v.getValue();
      this.V_SET(_name, _eContainer, _value);
      final Expression value = v.getValue();
      CharSequence _xifexpression = null;
      ComparisonOperator _coperator = value.getCoperator();
      boolean _equals = Objects.equal(_coperator, ComparisonOperator.NEW);
      if (_equals) {
        CharSequence _xblockexpression_1 = null;
        {
          Expression _value_1 = v.getValue();
          Object _value_2 = _value_1.getValue();
          final String type = _value_2.toString();
          String _name_1 = v.getName();
          EObject _eContainer_1 = v.eContainer();
          FTLCompiler.V_SET(_name_1, _eContainer_1, type);
          StringConcatenation _builder = new StringConcatenation();
          String _importedName = this.importManager.getImportedName(type, true);
          _builder.append(_importedName, "");
          _builder.append(" ");
          String _name_2 = v.getName();
          _builder.append(_name_2, "");
          _builder.append(" = ");
          _builder.append(this.modelName, "");
          _builder.append("Factory.eINSTANCE.create");
          _builder.append(type, "");
          _builder.append("();");
          _xblockexpression_1 = (_builder);
        }
        _xifexpression = _xblockexpression_1;
      } else {
        CharSequence _xblockexpression_2 = null;
        {
          String _xifexpression_1 = null;
          boolean _or = false;
          String _returnType = v.getReturnType();
          boolean _equals_1 = Objects.equal(_returnType, null);
          if (_equals_1) {
            _or = true;
          } else {
            String _returnType_1 = v.getReturnType();
            boolean _equals_2 = _returnType_1.equals("Nothing");
            _or = (_equals_1 || _equals_2);
          }
          if (_or) {
            String _resolveReturnType = this.resolveReturnType(value);
            _xifexpression_1 = _resolveReturnType;
          } else {
            String _returnType_2 = v.getReturnType();
            _xifexpression_1 = _returnType_2;
          }
          final String type = _xifexpression_1;
          String _name_1 = v.getName();
          EObject _eContainer_1 = v.eContainer();
          FTLCompiler.V_SET(_name_1, _eContainer_1, type);
          StringConcatenation _builder = new StringConcatenation();
          String _importedName = this.importManager.getImportedName(type, true);
          _builder.append(_importedName, "");
          _builder.append(" ");
          String _name_2 = v.getName();
          _builder.append(_name_2, "");
          _builder.append(" = ");
          String _generateExpression = this.generateExpression(value);
          _builder.append(_generateExpression, "");
          _builder.append(";");
          _xblockexpression_2 = (_builder);
        }
        _xifexpression = _xblockexpression_2;
      }
      _xblockexpression = (_xifexpression);
    }
    return _xblockexpression;
  }
  
  /**
   * Generates the code for the given {@link Assignment}.
   */
  protected CharSequence _generateStatement(final Assignment a) {
    CharSequence _xifexpression = null;
    String _operator = a.getOperator();
    boolean _equals = Objects.equal(_operator, "+=");
    if (_equals) {
      StringConcatenation _builder = new StringConcatenation();
      ModelElement _modelVariable = a.getModelVariable();
      String _name = _modelVariable.getName();
      String _createGetter = this.createGetter(_name);
      _builder.append(_createGetter, "");
      _builder.append(".add(");
      Expression _assignment = a.getAssignment();
      String _generateExpression = this.generateExpression(_assignment);
      _builder.append(_generateExpression, "");
      _builder.append(");");
      _xifexpression = _builder;
    } else {
      CharSequence _xifexpression_1 = null;
      String _operator_1 = a.getOperator();
      boolean _equals_1 = Objects.equal(_operator_1, "-=");
      if (_equals_1) {
        StringConcatenation _builder_1 = new StringConcatenation();
        ModelElement _modelVariable_1 = a.getModelVariable();
        String _name_1 = _modelVariable_1.getName();
        String _createGetter_1 = this.createGetter(_name_1);
        _builder_1.append(_createGetter_1, "");
        _builder_1.append(".remove(");
        Expression _assignment_1 = a.getAssignment();
        String _generateExpression_1 = this.generateExpression(_assignment_1);
        _builder_1.append(_generateExpression_1, "");
        _builder_1.append(");");
        _xifexpression_1 = _builder_1;
      } else {
        CharSequence _xifexpression_2 = null;
        Expression _assignment_2 = a.getAssignment();
        ComparisonOperator _coperator = _assignment_2.getCoperator();
        boolean _equals_2 = Objects.equal(_coperator, ComparisonOperator.NEW);
        if (_equals_2) {
          CharSequence _xblockexpression = null;
          {
            Expression _assignment_3 = a.getAssignment();
            final Object type = _assignment_3.getValue();
            StringConcatenation _builder_2 = new StringConcatenation();
            _builder_2.append(type, "");
            _builder_2.append(" ");
            ModelElement _modelVariable_2 = a.getModelVariable();
            _builder_2.append(_modelVariable_2, "");
            _builder_2.append(" = ");
            _builder_2.append(this.modelName, "");
            _builder_2.append("Factory.eINSTANCE.create");
            _builder_2.append(type, "");
            _builder_2.append("();");
            _xblockexpression = (_builder_2);
          }
          _xifexpression_2 = _xblockexpression;
        } else {
          StringConcatenation _builder_2 = new StringConcatenation();
          ModelElement _modelVariable_2 = a.getModelVariable();
          String _name_2 = _modelVariable_2.getName();
          String _createSetter = this.createSetter(_name_2);
          _builder_2.append(_createSetter, "");
          _builder_2.append("(");
          Expression _assignment_3 = a.getAssignment();
          String _generateExpression_2 = this.generateExpression(_assignment_3);
          _builder_2.append(_generateExpression_2, "");
          _builder_2.append(");");
          _xifexpression_2 = _builder_2;
        }
        _xifexpression_1 = _xifexpression_2;
      }
      _xifexpression = _xifexpression_1;
    }
    return _xifexpression;
  }
  
  /**
   * Generates the code for the given {@link If} statement.
   */
  protected CharSequence _generateStatement(final If i) {
    StringConcatenation _builder = new StringConcatenation();
    _builder.append("if (");
    Expression _expression = i.getExpression();
    String _generateExpression = this.generateExpression(_expression);
    _builder.append(_generateExpression, "");
    _builder.append(") {");
    _builder.newLineIfNotEmpty();
    {
      EList<Statement> _left = i.getLeft();
      boolean _hasElements = false;
      for(final Statement s : _left) {
        if (!_hasElements) {
          _hasElements = true;
        } else {
          _builder.appendImmediate("\n", "	");
        }
        _builder.append("\t");
        CharSequence _generateStatement = this.generateStatement(s);
        _builder.append(_generateStatement, "	");
        _builder.newLineIfNotEmpty();
      }
    }
    _builder.append("}");
    {
      EList<Statement> _right = i.getRight();
      boolean _isNullOrEmpty = IterableExtensions.isNullOrEmpty(_right);
      boolean _not = (!_isNullOrEmpty);
      if (_not) {
        _builder.append(" else {");
        _builder.newLineIfNotEmpty();
        {
          EList<Statement> _right_1 = i.getRight();
          boolean _hasElements_1 = false;
          for(final Statement s_1 : _right_1) {
            if (!_hasElements_1) {
              _hasElements_1 = true;
            } else {
              _builder.appendImmediate("\n", "	");
            }
            _builder.append("\t");
            CharSequence _generateStatement_1 = this.generateStatement(s_1);
            _builder.append(_generateStatement_1, "	");
            _builder.newLineIfNotEmpty();
          }
        }
        _builder.append("}");
      }
    }
    _builder.newLineIfNotEmpty();
    return _builder;
  }
  
  /**
   * Generates the code for the given {@link For} loop.
   */
  protected CharSequence _generateStatement(final For f) {
    CharSequence _xblockexpression = null;
    {
      String _iterator = f.getIterator();
      ModelElement _collection = f.getCollection();
      FTLCompiler.V_SET(_iterator, f, _collection);
      String _type = f.getType();
      boolean _notEquals = (!Objects.equal(_type, null));
      if (_notEquals) {
        String _iterator_1 = f.getIterator();
        String _type_1 = f.getType();
        FTLCompiler.V_SET(_iterator_1, f, _type_1);
      }
      String _xifexpression = null;
      String _type_2 = f.getType();
      boolean _notEquals_1 = (!Objects.equal(_type_2, null));
      if (_notEquals_1) {
        String _type_3 = f.getType();
        _xifexpression = _type_3;
      } else {
        String _xblockexpression_1 = null;
        {
          ModelElement _collection_1 = f.getCollection();
          String _name = _collection_1.getName();
          String genericType = FTLCompiler.V_GET(_name);
          String _xifexpression_1 = null;
          boolean _equals = Objects.equal(genericType, null);
          if (_equals) {
            ModelElement _collection_2 = f.getCollection();
            String _name_1 = _collection_2.getName();
            String _inferType = FTLCompiler.inferType(_name_1);
            _xifexpression_1 = _inferType;
          } else {
            _xifexpression_1 = genericType;
          }
          String resolvedType = _xifexpression_1;
          String _xifexpression_2 = null;
          boolean _endsWith = resolvedType.endsWith(">");
          boolean _not = (!_endsWith);
          if (_not) {
            _xifexpression_2 = resolvedType;
          } else {
            int _lastIndexOf = resolvedType.lastIndexOf("<");
            int _plus = (_lastIndexOf + 1);
            int _length = resolvedType.length();
            int _minus = (_length - 1);
            String _substring = resolvedType.substring(_plus, _minus);
            _xifexpression_2 = _substring;
          }
          _xblockexpression_1 = (_xifexpression_2);
        }
        _xifexpression = _xblockexpression_1;
      }
      final String type = _xifexpression;
      StringConcatenation _builder = new StringConcatenation();
      _builder.append("for (");
      String _importedName = this.importManager.getImportedName(type, true);
      _builder.append(_importedName, "");
      _builder.append(" ");
      String _iterator_2 = f.getIterator();
      _builder.append(_iterator_2, "");
      _builder.append(" : ");
      ModelElement _collection_1 = f.getCollection();
      String _name = _collection_1.getName();
      String _createGetter = this.createGetter(_name);
      _builder.append(_createGetter, "");
      _builder.append(") {");
      _builder.newLineIfNotEmpty();
      {
        EList<Statement> _body = f.getBody();
        for(final Statement s : _body) {
          _builder.append("\t");
          CharSequence _generateStatement = this.generateStatement(s);
          _builder.append(_generateStatement, "	");
          _builder.newLineIfNotEmpty();
        }
      }
      _builder.append("}");
      _builder.newLine();
      final CharSequence code = _builder;
      FTLCompiler.V_EVICT(f);
      _xblockexpression = (code);
    }
    return _xblockexpression;
  }
  
  /**
   * Generates the code for the given {@link Expression}.
   */
  public String generateExpression(final Expression e) {
    String _xblockexpression = null;
    {
      boolean _equals = Objects.equal(e, null);
      if (_equals) {
        return "";
      }
      StringBuilder _stringBuilder = new StringBuilder();
      final StringBuilder code = _stringBuilder;
      Object _value = e.getValue();
      boolean _notEquals = (!Objects.equal(_value, null));
      if (_notEquals) {
        ComparisonOperator _coperator = e.getCoperator();
        boolean _equals_1 = Objects.equal(_coperator, ComparisonOperator.NEW);
        if (_equals_1) {
          Object _value_1 = e.getValue();
          final String type = _value_1.toString();
          StringConcatenation _builder = new StringConcatenation();
          _builder.append(this.modelName, "");
          _builder.append("Factory.eINSTANCE.create");
          _builder.append(type, "");
          _builder.append("()");
          code.append(_builder.toString());
        } else {
          Object _value_2 = e.getValue();
          String _string = _value_2.toString();
          String _createGetter = this.createGetter(_string);
          code.append(_createGetter);
        }
      } else {
        FunctionCall _invocation = e.getInvocation();
        boolean _notEquals_1 = (!Objects.equal(_invocation, null));
        if (_notEquals_1) {
          FunctionCall _invocation_1 = e.getInvocation();
          CharSequence _generateStatement = this.generateStatement(_invocation_1);
          code.append(_generateStatement);
        }
      }
      Expression _next = e.getNext();
      boolean _notEquals_2 = (!Objects.equal(_next, null));
      if (_notEquals_2) {
        ComparisonOperator _coperator_1 = e.getCoperator();
        String _literal = _coperator_1.getLiteral();
        boolean _notEquals_3 = (!Objects.equal(_literal, "!"));
        if (_notEquals_3) {
          StringBuilder _append = code.append(" ");
          ComparisonOperator _coperator_2 = e.getCoperator();
          String _literal_1 = _coperator_2.getLiteral();
          StringBuilder _append_1 = _append.append(_literal_1);
          _append_1.append(" ");
        } else {
          LogicalOperator _loperator = e.getLoperator();
          String _literal_2 = _loperator.getLiteral();
          boolean _notEquals_4 = (!Objects.equal(_literal_2, "^"));
          if (_notEquals_4) {
            StringBuilder _append_2 = code.append(" ");
            LogicalOperator _loperator_1 = e.getLoperator();
            String _literal_3 = _loperator_1.getLiteral();
            StringBuilder _append_3 = _append_2.append(_literal_3);
            _append_3.append(" ");
          }
        }
        Expression _next_1 = e.getNext();
        String _generateExpression = this.generateExpression(_next_1);
        code.append(((String) _generateExpression));
      }
      String _string_1 = code.toString();
      _xblockexpression = (_string_1);
    }
    return _xblockexpression;
  }
  
  /**
   * Generates the code for retrieving the given property or nested property.
   */
  public String createGetter(final String name) {
    String _xblockexpression = null;
    {
      final String[] parts = name.split("\\.");
      int _size = ((List<String>)Conversions.doWrapArray(parts)).size();
      boolean _equals = (_size == 1);
      if (_equals) {
        return name;
      }
      String _get = ((List<String>)Conversions.doWrapArray(parts)).get(0);
      final String variable = FTLCompiler.V_GET(_get);
      StringConcatenation _builder = new StringConcatenation();
      _builder.append("Variable or method ");
      String _get_1 = ((List<String>)Conversions.doWrapArray(parts)).get(0);
      _builder.append(_get_1, "");
      _builder.append(" undefined or not visible in scope");
      final String msg = _builder.toString();
      boolean _equals_1 = Objects.equal(variable, null);
      if (_equals_1) {
        IllegalArgumentException _illegalArgumentException = new IllegalArgumentException(msg);
        throw _illegalArgumentException;
      }
      String _get_2 = ((List<String>)Conversions.doWrapArray(parts)).get(0);
      StringBuilder _stringBuilder = new StringBuilder(_get_2);
      final StringBuilder code = _stringBuilder;
      int _size_1 = ((List<String>)Conversions.doWrapArray(parts)).size();
      int _minus = (_size_1 - 1);
      IntegerRange _upTo = new IntegerRange(1, _minus);
      for (final int i : _upTo) {
        String _get_3 = ((List<String>)Conversions.doWrapArray(parts)).get(i);
        boolean _isMethodCall = this.isMethodCall(_get_3);
        if (_isMethodCall) {
          StringBuilder _append = code.append(".");
          String _get_4 = ((List<String>)Conversions.doWrapArray(parts)).get(i);
          _append.append(_get_4);
        } else {
          String _get_5 = ((List<String>)Conversions.doWrapArray(parts)).get(i);
          boolean _equals_2 = _get_5.equals("parent");
          if (_equals_2) {
            String _get_6 = ((List<String>)Conversions.doWrapArray(parts)).get(0);
            StringBuilder _stringBuilder_1 = new StringBuilder(_get_6);
            final StringBuilder typeName = _stringBuilder_1;
            IntegerRange _upTo_1 = new IntegerRange(1, i);
            for (final int j : _upTo_1) {
              StringBuilder _append_1 = typeName.append(".");
              String _get_7 = ((List<String>)Conversions.doWrapArray(parts)).get(j);
              _append_1.append(_get_7);
            }
            StringConcatenation _builder_1 = new StringConcatenation();
            _builder_1.append("((");
            String _string = typeName.toString();
            String _inferType = FTLCompiler.inferType(_string);
            _builder_1.append(_inferType, "");
            _builder_1.append(") ");
            code.insert(0, _builder_1.toString());
            code.append(".eContainer())");
          } else {
            StringBuilder _append_2 = code.append(".get");
            String _get_8 = ((List<String>)Conversions.doWrapArray(parts)).get(i);
            String _firstUpper = StringExtensions.toFirstUpper(_get_8);
            StringBuilder _append_3 = _append_2.append(_firstUpper);
            _append_3.append("()");
          }
        }
      }
      String _string_1 = code.toString();
      _xblockexpression = (_string_1);
    }
    return _xblockexpression;
  }
  
  /**
   * Generates the code for modifying the given property or nested property.
   */
  public String createSetter(final String name) {
    String _xblockexpression = null;
    {
      final String[] parts = name.split("\\.");
      int _size = ((List<String>)Conversions.doWrapArray(parts)).size();
      boolean _equals = (_size == 1);
      if (_equals) {
        return name;
      }
      String _get = ((List<String>)Conversions.doWrapArray(parts)).get(0);
      final String variable = FTLCompiler.V_GET(_get);
      StringConcatenation _builder = new StringConcatenation();
      _builder.append("Variable or method ");
      String _get_1 = ((List<String>)Conversions.doWrapArray(parts)).get(0);
      _builder.append(_get_1, "");
      _builder.append(" undefined or not visible in scope");
      final String msg = _builder.toString();
      boolean _equals_1 = Objects.equal(variable, null);
      if (_equals_1) {
        IllegalArgumentException _illegalArgumentException = new IllegalArgumentException(msg);
        throw _illegalArgumentException;
      }
      String _get_2 = ((List<String>)Conversions.doWrapArray(parts)).get(0);
      StringBuilder _stringBuilder = new StringBuilder(_get_2);
      final StringBuilder code = _stringBuilder;
      int _size_1 = ((List<String>)Conversions.doWrapArray(parts)).size();
      boolean _greaterThan = (_size_1 > 2);
      if (_greaterThan) {
        int _size_2 = ((List<String>)Conversions.doWrapArray(parts)).size();
        int _minus = (_size_2 - 2);
        IntegerRange _upTo = new IntegerRange(1, _minus);
        for (final int i : _upTo) {
          String _get_3 = ((List<String>)Conversions.doWrapArray(parts)).get(i);
          boolean _isMethodCall = this.isMethodCall(_get_3);
          if (_isMethodCall) {
            StringBuilder _append = code.append(".");
            String _get_4 = ((List<String>)Conversions.doWrapArray(parts)).get(i);
            _append.append(_get_4);
          } else {
            StringBuilder _append_1 = code.append(".get");
            String _get_5 = ((List<String>)Conversions.doWrapArray(parts)).get(i);
            String _firstUpper = StringExtensions.toFirstUpper(_get_5);
            StringBuilder _append_2 = _append_1.append(_firstUpper);
            _append_2.append("()");
          }
        }
      }
      StringBuilder _append_3 = code.append(".set");
      String _last = Iterables.<String>getLast(((Iterable<String>)Conversions.doWrapArray(parts)));
      String _firstUpper_1 = StringExtensions.toFirstUpper(_last);
      _append_3.append(_firstUpper_1);
      String _string = code.toString();
      _xblockexpression = (_string);
    }
    return _xblockexpression;
  }
  
  /**
   * Checks whether the given expression is qualified or unqualified method invocation.
   */
  private boolean isMethodCall(final String e) {
    boolean _endsWith = e.endsWith(")");
    return _endsWith;
  }
  
  public CharSequence generateFunction(final Function c) {
    if (c instanceof ConstructorFunction) {
      return _generateFunction((ConstructorFunction)c);
    } else if (c != null) {
      return _generateFunction(c);
    } else {
      throw new IllegalArgumentException("Unhandled parameter types: " +
        Arrays.<Object>asList(c).toString());
    }
  }
  
  public CharSequence generateStatement(final Statement a) {
    if (a instanceof Assignment) {
      return _generateStatement((Assignment)a);
    } else if (a instanceof For) {
      return _generateStatement((For)a);
    } else if (a instanceof FunctionCall) {
      return _generateStatement((FunctionCall)a);
    } else if (a instanceof If) {
      return _generateStatement((If)a);
    } else if (a instanceof ReturnStatement) {
      return _generateStatement((ReturnStatement)a);
    } else if (a instanceof Variable) {
      return _generateStatement((Variable)a);
    } else {
      throw new IllegalArgumentException("Unhandled parameter types: " +
        Arrays.<Object>asList(a).toString());
    }
  }
}
